package com.sinosoft.common.encrypt.filter;

import cn.hutool.core.util.ObjectUtil;
import com.sinosoft.common.core.constant.HeaderConstants;
import com.sinosoft.common.core.constant.HttpStatus;
import com.sinosoft.common.core.enums.ErrorCodeEnum;
import com.sinosoft.common.core.exception.GlobalAuthException;
import com.sinosoft.common.core.exception.ServiceException;
import com.sinosoft.common.core.utils.SpringUtils;
import com.sinosoft.common.core.utils.StringUtils;
import com.sinosoft.common.encrypt.annotation.ApiEncrypt;
import com.sinosoft.common.encrypt.properties.ApiDecryptProperties;
import com.sinosoft.common.redis.utils.RedisUtils;
import com.sinosoft.common.core.constant.CacheNames;
import com.sinosoft.system.api.domain.vo.RemoteClientVo;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.io.IOException;
import java.nio.charset.StandardCharsets;

/**
 * 统一加密过滤器
 *
 * @author zzf
 */
@Slf4j
public class CryptoFilter implements Filter {

    private final ApiDecryptProperties properties;

    public CryptoFilter(ApiDecryptProperties properties) {
        this.properties = properties;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest servletRequest = (HttpServletRequest) request;
        HttpServletResponse servletResponse = (HttpServletResponse) response;
        try {
            String authMode = servletRequest.getHeader(HeaderConstants.AUTH_MODE);
            if (StringUtils.isEmpty(authMode)) {
                authMode = HeaderConstants.AUTH_MODE_JWT;
            }
            if (StringUtils.isNotEmpty(authMode)) {
                authMode = authMode.toUpperCase();
                // 如果是JWT加密
                if (HeaderConstants.AUTH_MODE_JWT.equals(authMode)) {
                    // 走传统CryptoFilter逻辑 (基于@ApiEncrypt注解 + RSA/AES 或 SM2/SM4)
                    handleTraditionalEncryption(servletRequest, servletResponse, chain);
                } else if (HeaderConstants.AUTH_MODE_AKSK.equals(authMode)) {
                    // 走GlobalApiFilter逻辑 (基于clientId + SM2/SM4)
                    handleGlobalApiEncryption(servletRequest, servletResponse, chain);
                }
            } else {
                // 如果没有指定认证方式，直接放行
                chain.doFilter(request, response);
            }
        } catch (GlobalAuthException e) {
            handleException(servletResponse, e);
        } catch (Exception e) {
            log.error("请求处理失败", e);
            handleException(servletResponse, new GlobalAuthException(HttpStatus.ERROR, ErrorCodeEnum.SYSTEM_INTERNAL_ERROR.getCode(), "系统处理异常", e.getMessage()));
        }
    }

    /**
     * 处理传统加密逻辑
     * 支持两种模式：
     */
    private void handleTraditionalEncryption(HttpServletRequest servletRequest, HttpServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        // 检查是否为安全模式
        if (HeaderConstants.CRYPTO_TYPE_GM.equals(properties.getCryptoType())) {
            // 使用SM2/SM4加密方式
            handleJwtSafeModeEncryption(servletRequest, servletResponse, chain);
        } else {
            // 使用传统RSA/AES加密方式
            handleJwtTraditionalEncryption(servletRequest, servletResponse, chain);
        }
    }

    /**
     * 处理JWT安全模式加密
     */
    private void handleJwtSafeModeEncryption(HttpServletRequest servletRequest, HttpServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        ServletRequest requestWrapper = null;
        ServletResponse responseWrapper = null;
        JwtSafeModeResponseBodyWrapper responseBodyWrapper = null;
        // 获取请求头信息
        String nonce = servletRequest.getHeader(HeaderConstants.NONCE);
        String encryptKey = servletRequest.getHeader(HeaderConstants.ENCRYPT_KEY);
        // 是否为 PUT 或者 POST 请求
        if (HttpMethod.PUT.matches(servletRequest.getMethod()) || HttpMethod.POST.matches(servletRequest.getMethod())) {
            // 检查是否有加密密钥头
            if (StringUtils.isNotBlank(encryptKey)) {
                // 请求解密
                requestWrapper = new JwtSafeModeRequestWrapper(servletRequest, properties);
            }
        }
        // 如果有加密密钥，就进行响应加密
        if (StringUtils.isNotBlank(encryptKey)) {
            responseBodyWrapper = new JwtSafeModeResponseBodyWrapper(servletResponse);
            responseWrapper = responseBodyWrapper;
        }
        // 继续过滤器链
        chain.doFilter(
            ObjectUtil.defaultIfNull(requestWrapper, servletRequest),
            ObjectUtil.defaultIfNull(responseWrapper, servletResponse)
        );
        // 处理加密响应
        if (responseBodyWrapper != null) {
            servletResponse.reset();
            String responseContent = responseBodyWrapper.getEncryptContent(servletResponse, nonce, encryptKey, properties);
            writeResponse(servletResponse, responseContent);
        }
    }

    /**
     * 处理JWT传统加密
     */
    private void handleJwtTraditionalEncryption(HttpServletRequest servletRequest, HttpServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        boolean responseFlag = false;
        ServletRequest requestWrapper = null;
        ServletResponse responseWrapper = null;
        EncryptResponseBodyWrapper responseBodyWrapper = null;
        // 是否为 PUT 或者 POST 请求
        if (HttpMethod.PUT.matches(servletRequest.getMethod()) || HttpMethod.POST.matches(servletRequest.getMethod())) {
            if (properties.getEnabled()) {
                // 获取加密注解
                ApiEncrypt apiEncrypt = this.getApiEncryptAnnotation(servletRequest);
                responseFlag = apiEncrypt != null && apiEncrypt.response();
                // 是否存在加密标头
                String headerValue = servletRequest.getHeader(properties.getHeaderFlag());
                if (StringUtils.isNotBlank(headerValue)) {
                    // 请求解密
                    requestWrapper = new DecryptRequestBodyWrapper(servletRequest, properties.getPrivateKey(), properties.getHeaderFlag());
                } else {
                    // 是否有注解，有就报错，没有放行
                    if (ObjectUtil.isNotNull(apiEncrypt)) {
                        HandlerExceptionResolver exceptionResolver = SpringUtils.getBean("handlerExceptionResolver", HandlerExceptionResolver.class);
                        exceptionResolver.resolveException(servletRequest, servletResponse, null, new ServiceException("没有访问权限，请联系管理员授权", HttpStatus.FORBIDDEN));
                        return;
                    }
                }
            }
        }
        // 判断是否响应加密
        if (responseFlag) {
            responseBodyWrapper = new EncryptResponseBodyWrapper(servletResponse);
            responseWrapper = responseBodyWrapper;
        }
        chain.doFilter(
            ObjectUtil.defaultIfNull(requestWrapper, servletRequest),
            ObjectUtil.defaultIfNull(responseWrapper, servletResponse)
        );
        if (responseFlag) {
            servletResponse.reset();
            // 对原始内容加密
            String encryptContent = responseBodyWrapper.getEncryptContent(servletResponse, properties.getPublicKey(), properties.getHeaderFlag());
            // 对加密后的内容写出
            servletResponse.getWriter().write(encryptContent);
        }
    }

    /**
     * 处理GlobalApi加密逻辑
     * 基于clientId配置和SM2/SM4加密
     */
    private void handleGlobalApiEncryption(HttpServletRequest servletRequest, HttpServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        ServletRequest requestWrapper = null;
        ServletResponse responseWrapper = null;
        GlobalApiResponseBodyWrapper responseBodyWrapper = null;
        // 获取请求头信息
        String safeMode = servletRequest.getHeader(HeaderConstants.SAFE_MODE);
        String clientId = servletRequest.getHeader(HeaderConstants.CLIENT_ID);
        String nonce = servletRequest.getHeader(HeaderConstants.NONCE);
        String encryptKey = servletRequest.getHeader(HeaderConstants.ENCRYPT_KEY);

        RemoteClientVo clientVo = null;
        if (StringUtils.isNotBlank(clientId)) {
            // 获取客户端配置
            String clientCacheKey = CacheNames.SYS_CLIENT + ":" + clientId;
            clientVo = RedisUtils.getCacheObject(clientCacheKey);
            if (clientVo == null) {
                throw new GlobalAuthException(HttpStatus.FORBIDDEN, ErrorCodeEnum.AUTH_CLIENT_MISMATCH.getCode(), "无效的客户端", "客户端未授权，请联系管理员");
            }
            requestWrapper = new GlobalApiRequestWrapper(servletRequest, safeMode, clientVo);
            responseBodyWrapper = new GlobalApiResponseBodyWrapper(servletResponse);
            responseWrapper = responseBodyWrapper;
        }
        // 继续过滤器链
        chain.doFilter(
            ObjectUtil.defaultIfNull(requestWrapper, servletRequest),
            ObjectUtil.defaultIfNull(responseWrapper, servletResponse)
        );
        // 处理加密响应
        if (responseBodyWrapper != null) {
            servletResponse.reset();
            String responseContent = responseBodyWrapper.getResponseContent(servletResponse, nonce, safeMode, encryptKey, clientVo);
            writeResponse(servletResponse, responseContent);
        }
    }

    /**
     * 获取 ApiEncrypt 注解
     */
    private ApiEncrypt getApiEncryptAnnotation(HttpServletRequest servletRequest) {
        RequestMappingHandlerMapping handlerMapping = SpringUtils.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class);
        // 获取注解
        try {
            HandlerExecutionChain mappingHandler = handlerMapping.getHandler(servletRequest);
            if (ObjectUtil.isNotNull(mappingHandler)) {
                Object handler = mappingHandler.getHandler();
                if (ObjectUtil.isNotNull(handler)) {
                    // 从handler获取注解
                    if (handler instanceof HandlerMethod handlerMethod) {
                        return handlerMethod.getMethodAnnotation(ApiEncrypt.class);
                    }
                }
            }
        } catch (Exception e) {
            return null;
        }
        return null;
    }

    /**
     * 处理异常响应
     */
    private void handleException(HttpServletResponse response, GlobalAuthException e) throws IOException {
        response.setStatus(e.getCode() != null ? e.getCode() : HttpStatus.ERROR);
        writeResponse(response, e.toString());
    }

    /**
     * 写入响应内容
     */
    private void writeResponse(HttpServletResponse response, String content) throws IOException {
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        response.getWriter().write(content);
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}
